/********************************************************************
 Software License Agreement:

 The software supplied herewith by Microchip Technology Incorporated
 (the "Company") for its PIC(R) Microcontroller is intended and
 supplied to you, the Company's customer, for use solely and
 exclusively on Microchip PIC Microcontroller products. The
 software is owned by the Company and/or its supplier, and is
 protected under applicable copyright laws. All rights are reserved.
 Any use in violation of the foregoing restrictions may subject the
 user to criminal sanctions under applicable laws, as well as to
 civil liability for the breach of the terms and conditions of this
 license.

 THIS SOFTWARE IS PROVIDED IN AN "AS IS" CONDITION. NO WARRANTIES,
 WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED
 TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. THE COMPANY SHALL NOT,
 IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL OR
 CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
 *******************************************************************/

#include <p24FJ64GB002.h>
#include <system.h>
#include <system_config.h>
#include <usb/usb.h>// </editor-fold>
#include <stdio.h>

#include <leds.h>
#include "Rtcc.h"

//#include <buttons.h>
//#include <adc.h>

/** CONFIGURATION Bits **********************************************/
// CONFIG4
// CONFIG4
#pragma config DSWDTPS = DSWDTPS3       // DSWDT Postscale Select (1:128 (132 ms))
#pragma config DSWDTOSC = SOSC          // Deep Sleep Watchdog Timer Oscillator Select (DSWDT uses Secondary Oscillator (SOSC))
#pragma config RTCOSC = SOSC            // RTCC Reference Oscillator  Select (RTCC uses Secondary Oscillator (SOSC))
#pragma config DSBOREN = ON             // Deep Sleep BOR Enable bit (BOR enabled in Deep Sleep)
#pragma config DSWDTEN = OFF            // Deep Sleep Watchdog Timer (DSWDT disabled)

// CONFIG3
#pragma config WPFP = WPFP0             // Write Protection Flash Page Segment Boundary (Page 0 (0x0))
#pragma config SOSCSEL = SOSC           // Secondary Oscillator Pin Mode Select (SOSC pins in Default (high drive-strength) Oscillator Mode)
#pragma config WUTSEL = LEG             // Voltage Regulator Wake-up Time Select (Default regulator start-up time used)
#pragma config WPDIS = WPDIS            // Segment Write Protection Disable (Segmented code protection disabled)
#pragma config WPCFG = WPCFGDIS         // Write Protect Configuration Page Select (Last page and Flash Configuration words are unprotected)
#pragma config WPEND = WPENDMEM         // Segment Write Protection End Page Select (Write Protect from WPFP to the last page of memory)

// CONFIG2
#pragma config POSCMOD = NONE           // Primary Oscillator Select (Primary Oscillator disabled)
#pragma config I2C1SEL = PRI            // I2C1 Pin Select bit (Use default SCL1/SDA1 pins for I2C1 )
#pragma config IOL1WAY = OFF            // IOLOCK One-Way Set Enable (The IOLOCK bit can be set and cleared using the unlock sequence)
#pragma config OSCIOFNC = ON            // OSCO Pin Configuration (OSCO pin functions as port I/O (RA3))
#pragma config FCKSM = CSECME           // Clock Switching and Fail-Safe Clock Monitor (Sw Enabled, Mon Enabled)

//#pragma config FNOSC = FRCPLL           // Initial Oscillator Select (Fast RC Oscillator with Postscaler and PLL module (FRCPLL))
//#pragma config PLL96MHZ = ON            // 96MHz PLL Startup Select (96 MHz PLL Startup is enabled automatically on start-up)
#pragma config FNOSC = FRCDIV           // Initial Oscillator Select (Fast RC Oscillator with Postscaler and PLL module (FRCPLL))
#pragma config PLL96MHZ = OFF            // 96MHz PLL Startup Select (96 MHz PLL Startup is enabled automatically on start-up)

#pragma config PLLDIV = NODIV           // USB 96 MHz PLL Prescaler Select (Oscillator input used directly (4 MHz input))
#pragma config IESO = OFF               // Internal External Switchover (IESO mode (Two-Speed Start-up) disabled)

// CONFIG1
#pragma config WDTPS = PS1              // Watchdog Timer Postscaler (1:1)
#pragma config FWPSA = PR32             // WDT Prescaler (Prescaler ratio of 1:32)
#pragma config WINDIS = OFF             // Windowed WDT (Standard Watchdog Timer enabled,(Windowed-mode is disabled))
#pragma config FWDTEN = OFF             // Watchdog Timer (Watchdog Timer is disabled)
#pragma config ICS = PGx3               // Emulator Pin Placement Select bits (Emulator functions are shared with PGEC3/PGED3)
#pragma config GWRP = OFF               // General Segment Write Protect (Writes to program memory are allowed)
#pragma config GCP = OFF                // General Segment Code Protect (Code protection is disabled)
#pragma config JTAGEN = OFF             // JTAG Port Enable (JTAG port is disabled)

#define READ_LOW_BYTE(v)   ((unsigned char) (v))
#define READ_HIGH_BYTE(v)  ((unsigned char) (((unsigned int) (v)) >> 8))

unsigned long long ts=0;
unsigned long fcy = 0;

void __delay_us(unsigned long d) {
    __delay32( (unsigned long) ((d)*(fcy)/1000000ULL));
}

void __delay_ms(unsigned long d) {
    __delay32( (unsigned long) ((d)*(fcy)/1000ULL));
}


void print(char *s) {
    char *r = s;
    while((*r)!=0) {
        while(!U2STAbits.TRMT);
        U2TXREG = *r;
        r++;
    }

    while(!U2STAbits.TRMT);
}

void println(char *s) {
    print(s);

    while(!U2STAbits.TRMT);
    U2TXREG = '\n';
    while(!U2STAbits.TRMT);
    U2TXREG = '\r';

    while(!U2STAbits.TRMT);
}


void printtime() {

   GetTimeStamp();

   unsigned char min = timestamp[7];
   unsigned char sec = timestamp[6];

   unsigned char hour = timestamp[4];
   unsigned char month = timestamp[3];
   unsigned char day = timestamp[2];

   unsigned char year = timestamp[0];

   log("y%02xm%02xd%02xh%02xm%02xs%02x", year, month, day, hour, min, sec);
   //log("m%02xs%02x", min, sec);
}

void FastClock() {
   // FCY 16Mhz
   // PLL enabled

   //;Place the new oscillator selection in W0
   //;OSCCONH (high byte) Unlock Sequence
   asm("MOV #0x01, w0");

   asm("MOV #OSCCONH, w1");
   asm("MOV #0x78, w2");
   asm("MOV #0x9A, w3");
   asm("MOV.b w2, [w1]");
   asm("MOV.b w3, [w1]");
   //;Set new oscillator selection
   asm("MOV.b WREG, OSCCONH");
   //;OSCCONL (low byte) unlock sequence
   asm("MOV #OSCCONL, w1");
   asm("MOV #0x46, w2");
   asm("MOV #0x57, w3");
   asm("MOV.b w2, [w1]");
   asm("MOV.b w3, [w1]");
   //;Start oscillator switch operation
   asm("BSET OSCCON,#0");

   fcy = 16000000;
   
unsigned int pll_startup_counter = 600;
                CLKDIVbits.PLLEN = 1;
                while(pll_startup_counter--);


}

void SlowClock() {
   // FCY 2Mhz

   //;Place the new oscillator selection in W0
   //;OSCCONH (high byte) Unlock Sequence
   asm("MOV #0x07, w0");

   asm("MOV #OSCCONH, w1");
   asm("MOV #0x78, w2");
   asm("MOV #0x9A, w3");
   asm("MOV.b w2, [w1]");
   asm("MOV.b w3, [w1]");
   //;Set new oscillator selection
   asm("MOV.b WREG, OSCCONH");
   //;OSCCONL (low byte) unlock sequence
   asm("MOV #OSCCONL, w1");
   asm("MOV #0x46, w2");
   asm("MOV #0x57, w3");
   asm("MOV.b w2, [w1]");
   asm("MOV.b w3, [w1]");
   //;Start oscillator switch operation
   asm("BSET OSCCON,#0");

   fcy = 2000000;
}


void SetRTC() {
   mRtccAlrmDisable(); // need to disable before we can set a new alarm
   RtccWrOn();            		//write enable the rtcc registers
   RtccSetAlarmRpt(RTCC_RPT_TEN_SEC,1);	//Set the alarm repeat to every 10 seconds
   ALCFGRPTbits.CHIME = 0;
   mRtccOn();				//enable the rtcc
   mRtccAlrmEnable();			//enable the rtcc alarm to wake the device up from deep sleep
   mRtccSetInt(1); // enable RTCC interrupt with priority 1

   // Set alarm
   NVMKEY = 0x55;              // 55/AA sequence to enable write to RTCWREN
   NVMKEY = 0xAA;
   RCFGCALbits.RTCWREN = 1;    // enable write to RTC register
   ALCFGRPTbits.ALRMPTR = 3;
   ALRMVAL = 0x0101;
   ALRMVAL = 0x0101;
   ALRMVAL = 0x0101;
   ALRMVAL = 0x0100;
}

void USBOn() {

   U1PWRCbits.USBPWR = 1;
   U1CONbits.USBEN = 1;
   SYSTEM_Initialize(SYSTEM_STATE_USB_START);
   USBDeviceInit();
   USBDeviceAttach();

   // For some reason the USB device wants to reuse the B15 pin for its own use (VBUSST)
   PowerOpamp(true);
}

void USBOff() {

   //Blink(LED1, 5);

   USBDeviceDetach();
   // ANDRAS: DISABLE AND POWER OFF USB MODULE (50 uA)
   U1CONbits.USBEN = 0;
   U1PWRCbits.USBPWR = 0;

}

void USBStuff() {

        SYSTEM_Tasks();

        #if defined(USB_POLLING)
            // Interrupt or polling method.  If using polling, must call
            // this function periodically.  This function will take care
            // of processing and responding to SETUP transactions
            // (such as during the enumeration process when you first
            // plug in).  USB hosts require that USB devices should accept
            // and process SETUP packets in a timely fashion.  Therefore,
            // when using polling, this function should be called
            // regularly (such as once every 1.8ms or faster** [see
            // inline code comments in usb_device.c for explanation when
            // "or faster" applies])  In most cases, the USBDeviceTasks()
            // function does not take very long to execute (ex: <100
            // instruction cycles) before it returns.
            USBDeviceTasks();
        #endif

        /* If the USB device isn't configured yet, we can't really do anything
         * else since we don't have a host to talk to.  So jump back to the
         * top of the while loop. */
        if( USBGetDeviceState() < CONFIGURED_STATE )
        {
            /* Jump back to the top of the while loop. */

            return;
        }

        /* If we are currently suspended, then we need to see if we need to
         * issue a remote wakeup.  In either case, we shouldn't process any
         * keyboard commands since we aren't currently communicating to the host
         * thus just continue back to the start of the while loop. */
        if( USBIsDeviceSuspended() == true )
        {
            /* Jump back to the top of the while loop. */
            return;
        }

        //Application specific tasks
        APP_DeviceCustomHIDTasks();
}

void InitUART2(unsigned long rate, unsigned long fcy)
{
    unsigned long BAUDRATE2 = rate;
    unsigned long BAUDRATEREG2 = fcy/16/BAUDRATE2-1;


    __builtin_write_OSCCONL(OSCCON & 0xbf);
	// Configure Input Functions **********************
	// Assign UART2RX To Pin RP10
	// RPINR19bits.U2RXR = 10;
	// Configure Output Functions *********************
	// Assign UART2TX To Pin RP6
	RPOR1bits.RP3R = 5;
	// Lock Registers
	__builtin_write_OSCCONL(OSCCON | 0x40);

	// This is an EXAMPLE, so brutal typing goes into explaining all bit sets

	// The Explorer 16 board has a DB9 connector wired to UART2, so we will
	// be configuring this port only

	// configure U2MODE
	U2MODEbits.UARTEN = 0;	// Bit15 TX, RX DISABLED, ENABLE at end of func
	U2MODEbits.USIDL = 0;	// Bit13 Continue in Idle
	U2MODEbits.IREN = 0;	// Bit12 No IR translation
	U2MODEbits.RTSMD = 0;	// Bit11 Simplex Mode
	U2MODEbits.UEN = 0;		// Bits8,9 TX,RX enabled, CTS,RTS not
	U2MODEbits.WAKE = 0;	// Bit7 No Wake up (since we don't sleep here)
	U2MODEbits.LPBACK = 0;	// Bit6 No Loop Back
	U2MODEbits.ABAUD = 0;	// Bit5 No Autobaud (would require sending '55')
	U2MODEbits.RXINV = 0;	// Bit4 IdleState = 1
	U2MODEbits.BRGH = 0;	// Bit3 16 clocks per bit period
	U2MODEbits.PDSEL = 0;	// Bits1,2 8bit, No Parity
	U2MODEbits.STSEL = 0;	// Bit0 One Stop Bit

	U2BRG = BAUDRATEREG2;	// baud rate

	// Load all values in for U1STA SFR
	U2STAbits.UTXISEL1 = 0;	//Bit15 Int when Char is transferred (1/2 config!)
	U2STAbits.UTXINV = 0;	//Bit14 N/A, IRDA config
	U2STAbits.UTXISEL0 = 0;	//Bit13 Other half of Bit15
	U2STAbits.UTXBRK = 0;	//Bit11 Disabled
	U2STAbits.UTXEN = 0;	//Bit10 TX pins controlled by periph
	U2STAbits.UTXBF = 0;	//Bit9 *Read Only Bit*
	U2STAbits.TRMT = 0;		//Bit8 *Read Only bit*
	U2STAbits.URXISEL = 0;	//Bits6,7 Int. on character recieved
	U2STAbits.ADDEN = 0;	//Bit5 Address Detect Disabled
	U2STAbits.RIDLE = 0;	//Bit4 *Read Only Bit*
	U2STAbits.PERR = 0;		//Bit3 *Read Only Bit*
	U2STAbits.FERR = 0;		//Bit2 *Read Only Bit*
	U2STAbits.OERR = 0;		//Bit1 *Read Only Bit*
	U2STAbits.URXDA = 0;	//Bit0 *Read Only Bit*

	IFS1bits.U2TXIF = 0;	// Clear the Transmit Interrupt Flag
	IEC1bits.U2TXIE = 1;	// Enable Transmit Interrupts
	IFS1bits.U2RXIF = 0;	// Clear the Recieve Interrupt Flag
	IEC1bits.U2RXIE = 1;	// Enable Recieve Interrupts

	U2MODEbits.UARTEN = 1;	// And turn the peripheral on

	U2STAbits.UTXEN = 1;
}

void InitSPI(unsigned char spre, unsigned char ppre) {
    SPI1STATbits.SPIEN = 0;  // disable spi module (spicon registers are only writable if disabled)

    SPI1CON1bits.DISSCK = 0; // CLK enabled
    SPI1CON1bits.DISSDO = 0; // SDO pin is used
    SPI1CON1bits.MODE16 = 0; // 8 BIT MODE
    SPI1CON1bits.SMP = 0;    // sampled at the middle
    SPI1CON1bits.CKE = 0;    // MODE 1,1
    SPI1CON1bits.SSEN = 0;   // built-in chip select is not used
    SPI1CON1bits.CKP = 1;    // MODE 1,1
    SPI1CON1bits.MSTEN = 1;  // master mode
    SPI1CON1bits.SPRE = spre;   // secondary prescale  -- IMPORTANT: CANNOT BE TOO FAST, FLASH DOES NOT WORK PROPERLY
    SPI1CON1bits.PPRE = ppre;   // primary prescale

    SPI1CON2bits.FRMEN = 0;  // framing disabled
    SPI1CON2bits.SPIBEN = 0; // legacy mode

    SPI1STATbits.SPIROV = 0;
    SPI1STATbits.SPISIDL = 1;// module does not work in idle mode

    SPI1STATbits.SPIEN = 1;  // enable spi module
    
}

void InitT1() {
    // timer setup
    T1CON = 0x00; //Stops the Timer1 and reset control reg.
    TMR1 = 0x00; //Clear contents of the timer register
    PR1 = 32768 / DT; //Load the Period register 
    IPC0bits.T1IP = 7; // Prioty 7 highest
    
    IFS0bits.T1IF = 0; //Clear the Timer1 interrupt status flag
    IEC0bits.T1IE = 1; //Enable Timer1 interrupts
    T1CONbits.TON = 1; //Start Timer1 with prescaler settings at 1:1
    T1CONbits.TCS = 1; // external clock source
    T1CONbits.TCKPS = 0b00;

}

void TriggerT2(unsigned long i) {
    // timer setup
    T2CON = 0x00; //Stops the Timer2 and reset control reg.
    TMR2 = 0x00; //Clear contents of the timer register
    PR2 = i ;
    IPC1bits.T2IP = 0x01; //Setup Timer2 interrupt for desired priority level

    IFS0bits.T2IF = 0; //Clear the Timer2 interrupt status flag
    IEC0bits.T2IE = 1; //Enable Timer2 interrupts
    T2CONbits.TCKPS = 0b11; // prescaler settings at 1:256

    T2CONbits.TON = 1; //Start Timer2 
    T2CONbits.TCS = 0; // internal clock source
}

void InitPorts() {

     AD1PCFG = 0xffff; // all ports digital

    // SPI config

    // RB14 - SI - RP14
    // RB13 - SO - RP13

    __builtin_write_OSCCONL (OSCCON & 0xbf);
    RPINR20bits.SDI1R = 14; // assign SDI1 input to RP0
    RPOR1bits.RP2R = 8;    // assign RP2 to SCK1 (peripheral function 8)
    RPOR6bits.RP13R = 7;    // assign RP13 to SDO (peripheral function 7)
    __builtin_write_OSCCONL (OSCCON | 0x40);

    TRISBbits.TRISB2 = 0; // SCK output

    ADC_CS = 1;
    ADC_CS_TRIS = 0;

    MEM_CS = 1;
    MEM_CS_TRIS = 0;

    SDI_TRIS = 1;
    SDO_TRIS = 0;

    PWROPAMP = 1;
    PWROPAMP_TRIS = 0;

    PWR5V_TRIS = 1;
    PWR5V_ODC = 1;
    PWR5V_NEG = 1;
    PWR5V_TRIS = 0;

    USB_BUS_SENSE_TRIS = 1;

    LED_Enable(LED1);
    LED_Off(LED1);

    LED_Enable(LED2);
    LED_Off(LED2);

}

void PowerOpamp(bool b) {
    PWROPAMP_TRIS = 0;
    
    if(b) PWROPAMP = 1;
    else PWROPAMP = 0;    
}

unsigned char timestamp[8];
void GetTimeStamp() {
   unsigned int i;
   RCFGCALbits.RTCPTR = 3;

   i = RTCVAL;
   timestamp[0] = READ_LOW_BYTE(i);
   timestamp[1] = READ_HIGH_BYTE(i);

   i = RTCVAL;
   timestamp[2] = READ_LOW_BYTE(i);
   timestamp[3] = READ_HIGH_BYTE(i);

   i = RTCVAL;
   timestamp[4] = READ_LOW_BYTE(i);
   timestamp[5] = READ_HIGH_BYTE(i);

   i = RTCVAL;
   timestamp[6] = READ_LOW_BYTE(i);
   timestamp[7] = READ_HIGH_BYTE(i);
}


/*********************************************************************
* Function: void SYSTEM_Initialize( SYSTEM_STATE state )
*
* Overview: Initializes the system.
*
* PreCondition: None
*
* Input:  SYSTEM_STATE - the state to initialize the system into
*
* Output: None
*
********************************************************************/
void SYSTEM_Initialize( SYSTEM_STATE state )
{
    switch(state)
    {
        case SYSTEM_STATE_USB_START:
            //On the PIC24FJ64GB004 Family of USB microcontrollers, the PLL will not power up and be enabled
            //by default, even if a PLL enabled oscillator configuration is selected (such as HS+PLL).
            //This allows the device to power up at a lower initial operating frequency, which can be
            //advantageous when powered from a source which is not gauranteed to be adequate for 32MHz
            //operation.  On these devices, user firmware needs to manually set the CLKDIV<PLLEN> bit to
            //power up the PLL.
            {
                unsigned int pll_startup_counter = 600;
                CLKDIVbits.PLLEN = 1;
                while(pll_startup_counter--);
            }
            
            //LED_Enable(LED_USB_DEVICE_STATE);
            //LED_Enable(LED_USB_DEVICE_HID_CUSTOM);
            
            //BUTTON_Enable(BUTTON_USB_DEVICE_HID_CUSTOM);
            
            //ADC_SetConfiguration(ADC_CONFIGURATION_DEFAULT);
            //ADC_ChannelEnable(ADC_CHANNEL_POTENTIOMETER);
            break;
            
        case SYSTEM_STATE_USB_SUSPEND:
            break;
            
        case SYSTEM_STATE_USB_RESUME:
            break;
    }
}

#if defined(USB_INTERRUPT)
void __attribute__((interrupt,auto_psv)) _USB1Interrupt()
{
    USBDeviceTasks();
}
#endif

